/*
 * Author: Shai Wyborski
 *
 * Defines the molecule data structre.
 *
 */

#ifndef MOLECULE_H
#define MOLECULE_H

#include "Atom.h"

#include <string>
#include <iostream>
#include <boost/dynamic_bitset.hpp>
#include <openbabel/mol.h>
#include <openbabel/atom.h>
#include <openbabel/obconversion.h>
#include <cassert>
#include <functional>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <cmath>
#include <cassert>

using namespace boost;

#define DIM 3
#define MINDOOUBLE  1e-8
#define LINE_BUFFER_SIZE 1000  /* maximal length of line of input */
#define DEPTH_ITERATIONS 200   /* maximal depth to descend to when checking similarity */
#define TRUE 1
#define FALSE 0

typedef dynamic_bitset<> AtomSet;
typedef dynamic_bitset<>::size_type atom_ind;

class Molecule{

private:
	Molecule();  //private default constructor

public:

	/*
	 * Two atoms are in the same SymmetryClass iff, taking the subgraph from each
	 * atom which contains all atoms whose shortest path from the given atom is DEPTH_ITERATION
	 * produces isomorphic graphs for both atoms.
	 */
	class SymmetryClass
	{
	public:
		AtomSet* _set;
		std::list<Atom*> _atoms;

		~SymmetryClass()
		{
			delete(_set);
		}
	};

	Molecule(OpenBabel::OBMol &obmol);

	/*copy-ctor*/
	Molecule(Molecule& other): _size(other._size), _groupNum(other._groupNum),
			_norm(other._norm), _atoms(other._atoms), _classes(other._classes){}

	//Members
	int _size;			 // The size of the molecule
	int  _groupNum;      // the number of groups of similarity
	double _norm;	     // The normalization factor
	std::vector<Atom*> _atoms;
	std::vector<AtomSet*> _adjecancy; //The atoms in the set in _adjecancy[i] are the neighbours of _atoms[i]
	std::vector<SymmetryClass> _classes; //Symmetry classes
	~Molecule();

	//Functions

	/*
	 * Get an AtomSet of all atoms which match a certain criteria.
	 *
	 * property - a function which gets an atom and returns a boolean,
	 * 			  given an atom it determines weather or not it should belong to the set.
	 *
	 *
	 * action - an optional argument. A function which gets a reference to an Atom object
	 * 			and does something (anything) with it. Will be performed on any atom for
	 * 			which property returns true.
	 *
	 * return value: An AtomSet of exactly all atoms for which property returns true
	 *
	 */
	AtomSet getSetMask(std::function<bool (const Atom)> property, std::function<void (Atom&)> action);

	/*
	 * Same as getSetMask, only it halts at the first matching atom and returns it's index
	 */
	int findAtom(std::function<bool (const Atom)> property, std::function<void (Atom&)> action);

	/*
	 * Given two atom references, returns true iff they are of the same similarity class and
	 * they have neighbors of the same similarity class.
	 */
	bool isSimilar(const Atom &A, const Atom &B);

	/*
	 * Label each atom to a proper similarity class, according to what was described below
	 */
	//TODO: I think this could be moved to private as it's only used during construction
	int initSimilarity(int depth);

	/*
	 * Given two atom indices determine weather they are adjacent
	 */
	bool areAdj(int A, int B);

	/*
	 * Given a class ID return a reference to it's representing class
	 */
	SymmetryClass& getClass(int classId);

	/*
	 * Given a class ID return it's size
	 */
	int getClassSize(int classId);

	/*
	 * Get the size of the largest class
	 */
	int getMaxClassSize();

	/*
	 * Get the number of classes
	 */
	int numClasses();

	/*
	 * Normalize the molecule to be of unit size.
	 * if succeed return true. else return false.
	 */
	bool normalize();

	/*
	 * Print all the atoms.
	 */
	void printAtoms(AtomSet* set = NULL, int from = 0, int to = -1);
};

#endif
